#pragma once

#include <gtl/atom/atom.h>
#include <gtl/atom//mp4_detail.h>

namespace gtl
{

namespace atom
{

class mp4 : public atom, public save_to
{
public:
	mp4(ireader* reader_ptr, atom* parent_ptr, int64 offset, int64 length, 
				uint32 type, uint32 header_len, bool auto_parse = true)
		: atom(reader_ptr, parent_ptr, offset, length, type, header_len)
	{
		if(dynamic_cast<mp4*>(parent_ptr) != NULL)
			m_depth = parent_ptr->depth() + 1;

		if(auto_parse)
			parse();
	}

	virtual ~mp4(void)
	{
	}

public:
	void parse()
	{
		if(m_nodes.empty())
		{
			parse(0);
			mp4_details().parse(this);
		}
	}

protected:
	// Override atom
	virtual void parse(int64 offset)
	{
		if(m_type == 'mdat')
			return;

		if(m_type == 'mp4a' || m_type == 'avc1')
			return parse_avc1_or_mp4a(offset);

		offset += m_header_len;

		while(offset < m_length)
		{
			uchar hdr[8];
			uint32 header_len = 8;
			read(offset, 8, hdr);
			uint32 length = gtl::endian::get_be32(hdr);
			uint32 type = gtl::endian::get_be32(hdr + 4);

			if((!visible_character(uchar(type >> 24)) && !visible_character(uchar(type)))
				|| !visible_character(uchar(type >> 16))
				|| !visible_character(uchar(type >> 8)) )
			{
				offset += header_len;
				continue;
			}

			if(length == 1)
			{
				read(offset + 8, 8, hdr);
				length = gtl::endian::get_be64(hdr);
				header_len += 8;
			}
			else if(length == 0)
			{
				// whole remainder
				length = m_length - offset;
			}

			if(type == 'uuid')
				header_len += 16;

			if((length < 0) || ((offset + length) > (m_length)))
				break;

			mp4* node = new mp4(m_reader, this, m_offset + offset, length, type, header_len);
			m_nodes.push_back(node);

			offset += length;
		} // end while
	}

protected:
	virtual void parse_avc1_or_mp4a(int64 offset)
	{
		offset += m_header_len;

		uint32 length = 0;
		uint32 type = 0;

		while(offset < m_length)
		{
			long lHeader = 8;

			length <<= 8;
			*((char*)&length) = *(((uchar*)&type) + 3);
			type <<= 8;
			read(offset++, 1, ((uchar*)&type)) ;

			if(type != 'avcC' && type != 'esds'	&& type != 'btrt' )
				continue;

			offset -= 8;

			if((length < 0) || ((offset + length) > (m_length)))
				break;

			mp4* pChild = new mp4(m_reader, this, m_offset + offset, length, type, lHeader);
			m_nodes.push_back(pChild);

			offset += length;
		}
	}
};

//class mp4_parser
//{
//public:
//	mp4_parser(atom* pRootAtom);
//	virtual ~mp4_parser();
//
//public:
//	virtual void write_to_xml(atom* pAtom, const std::string& strOutputName, bool detail);
//
//protected:
//	virtual atom::values_type parse(atom* pAtom);
//	virtual void output_atom(TiXmlNode* pXmlNode, atom* pAtom, bool detail);
//	virtual void output_atom_info(TiXmlElement* pXmlElement, atom* pAtom, bool detail);
//
//private:
//	bool visible_character(unsigned char character)
//	{
//		return (character >= '0' && character <= '9') 
//			|| (character >= 'a' && character <= 'z') 
//			|| (character >= 'A' && character <= 'Z');
//	}
//};
//
} // end of namespace atom

} // end of namespace gtl